/*


  Simple Demo for GLSL 2.0

  www.lighthouse3d.com

*/
#include <stdio.h>
#include <stdlib.h>

#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/glext.h>

#include "textfile.h"

GLuint v,f,f2,p;
float lpos[4] = {1,0.5,1,0};
float rotationX = 0.0, rotationY = 0.0;
int   last_x, last_y;

void changeSize(int w, int h)
{
	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if(h == 0)
		h = 1;

	float ratio = 1.0* w / h;

	// Reset the coordinate system before modifying
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Set the viewport to be the entire window
   glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45,ratio,1,1000);
	glMatrixMode(GL_MODELVIEW);
}


void renderScene(void) {

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();
	gluLookAt(0.0,0.0,8.0,
		      0.0,0.0,-1.0,
			  0.0f,1.0f,0.0f);

	glLightfv(GL_LIGHT0, GL_POSITION, lpos);

	glPushMatrix();
		glTranslatef(0.0f, 1.5f, 0.0f);
	   glRotatef( rotationY, 0.0, 1.0, 0.0 );
   	glRotatef( rotationX, 1.0, 0.0, 0.0 );
		glutSolidTeapot(1);
	glPopMatrix();

	glPushMatrix();
		glTranslatef(-2.0f, -0.7f, 0.0f);
	   glRotatef( rotationY, 0.0, 1.0, 0.0 );
   	glRotatef( rotationX, 1.0, 0.0, 0.0 );
		glutSolidTorus(0.3f, 1.0f, 20, 20);
	glPopMatrix();

	glPushMatrix();
		glTranslatef(2.0f, -0.7f, 0.0f);
	   glRotatef( rotationY, 0.0, 1.0, 0.0 );
   	glRotatef( rotationX, 1.0, 0.0, 0.0 );
		glutSolidSphere(1.0, 20, 20);
	glPopMatrix();

	glutSwapBuffers();
}

void processNormalKeys(unsigned char key, int x, int y) {

	if (key == 27)
		exit(0);
}

GLint getUniLoc(GLuint program, const GLchar *name)
{
    GLint loc;

    loc = glGetUniformLocation(program, name);
    if (loc == -1)
        printf("No such uniform named \"%s\"\n", name);
    //printOpenGLError();  // Check for OpenGL errors
    return loc;
}

int installBrickShaders(const GLchar *brickVertex, const GLchar *brickFragment)
{
	GLuint brickVS, brickFS, brickProg;   // handles to objects
//   GLint  vertCompiled, fragCompiled;    // status values
//   GLint  linked;
	// Create a vertex shader object and a fragment shader object
	brickVS = glCreateShader(GL_VERTEX_SHADER);
	brickFS = glCreateShader(GL_FRAGMENT_SHADER);
	// Load source code strings into shaders
	glShaderSource(brickVS, 1, &brickVertex, NULL);
	glShaderSource(brickFS, 1, &brickFragment, NULL);
	// Compile the brick vertex shader and print out
	// the compiler log file.
	glCompileShader(brickVS);
	//printOpenGLError();  // Check for OpenGL errors
	//glGetShaderiv(brickVS, GL_COMPILE_STATUS, &vertCompiled);
	//printShaderInfoLog(brickVS);
	// Compile the brick fragment shader and print out
	// the compiler log file.
	glCompileShader(brickFS);
	//printOpenGLError();  // Check for OpenGL errors
	//glGetShaderiv(brickFS, GL_COMPILE_STATUS, &fragCompiled);
	//printShaderInfoLog(brickFS);
	//if (!vertCompiled || !fragCompiled)
	  	 //return 0;
	// Create a program object and attach the two compiled shaders
	brickProg = glCreateProgram();
	glAttachShader(brickProg, brickVS);
	glAttachShader(brickProg, brickFS);
	// Link the program object and print out the info log
	glLinkProgram(brickProg);
	//printOpenGLError();  // Check for OpenGL errors
	//glGetProgramiv(brickProg, GL_LINK_STATUS, &linked);
	//printProgramInfoLog(brickProg);
	//if (!linked)
	  //  return 0;

	// Install program object as part of current state
	glUseProgram(brickProg);

	// Set up initial uniform values
   glUniform3f(getUniLoc(brickProg, "BrickColor"), 1.0, 0.3, 0.2);
   glUniform3f(getUniLoc(brickProg, "MortarColor"), 0.85, 0.86, 0.84);
   glUniform2f(getUniLoc(brickProg, "BrickSize"), 0.30, 0.15);
   glUniform2f(getUniLoc(brickProg, "BrickPct"), 0.90, 0.85);
   glUniform3f(getUniLoc(brickProg, "LightPosition"), 0.0, 0.0, 4.0);

   return 1;
}



void setShaders()
{
	char *vs = NULL,*fs = NULL;
	v = glCreateShader(GL_VERTEX_SHADER);
	f = glCreateShader(GL_FRAGMENT_SHADER);

	vs = textFileRead((char*) "../projetos/shaderBrick/shaders/brick.vert");
	fs = textFileRead((char*) "../projetos/shaderBrick/shaders/brick.frag");

	if(!vs)
	{
		printf("File \"brick.vert\" not found.\n");
		exit(1);
	}

	if(!fs)
	{
		printf("File \"brick.frag\" not found.\n");
		exit(1);
	}

	const char * ff = fs;
	const char * vv = vs;

	installBrickShaders(vv, ff);

	/*
	glShaderSource(v, 1, &vv,NULL);
	glShaderSource(f, 1, &ff,NULL);

	free(vs);
	free(fs);

	glCompileShader(v);
	glCompileShader(f);

	p = glCreateProgram();
	glAttachShader(p,f);
	glAttachShader(p,v);

	glLinkProgram(p);
	glUseProgram(p);
	*/
}

// Motion callback
void motion(int x, int y )
{
  rotationX += (float) (y - last_y);
  rotationY += (float) (x - last_x);

  last_x = x;
  last_y = y;
}

int main(int argc, char **argv) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100,100);
	glutInitWindowSize(800,800);
	glutCreateWindow("MM 2004-05");

   glutMotionFunc( motion );
	glutDisplayFunc(renderScene);
	glutIdleFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutKeyboardFunc(processNormalKeys);

	glEnable(GL_DEPTH_TEST);
	glClearColor(1.0,1.0,1.0,1.0);

	glewInit();
	if (glewIsSupported("GL_VERSION_2_0"))
		printf("Ready for OpenGL 2.0\n");
	else {
		printf("OpenGL 2.0 not supported\n");
		exit(1);
	}

	setShaders();

	glutMainLoop();

	// just for compatibiliy purposes
	return 0;
}

